﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;

namespace FI.Core.Utilities
{
    /// <summary>
    /// The enumerable extensions
    /// </summary>
    public static class EnumerableExtensions
    {
        /// <summary>
        /// The enumerable source
        /// </summary>
        /// <param name="source"><see cref="System.Collections.Generic.IEnumerable{T}"/></param>
        /// <param name="propertyName">A property name of T</param>
        /// <param name="sortDirection"><see cref="FI.Core.SortDirection"/></param>
        public static IEnumerable<TSource> Sort<TSource>(this IEnumerable<TSource> source, string propertyName, SortDirection sortDirection = SortDirection.Ascending)
        {
            if (String.IsNullOrEmpty(propertyName))
            {
                return source;
            }

            var sourceType = typeof(TSource);
            propertyName = sourceType.GetPropertyName(propertyName);
            var propertyInfo = sourceType.GetProperty2(propertyName);
            var parameterExpression = Expression.Parameter(sourceType);
            var bodyExpression = Expression.Property(parameterExpression, propertyInfo);
            var selectorExpression = Expression.Lambda(bodyExpression, parameterExpression);

            var methodName = sortDirection == SortDirection.Ascending ? "OrderBy" : "OrderByDescending";
            var methodExpression = Expression.Call(typeof(Enumerable), methodName, new Type[] { sourceType, propertyInfo.PropertyType }, Expression.Constant(source), selectorExpression);
            return (IOrderedEnumerable<TSource>)methodExpression.Method.Invoke(null, new object[] { source, selectorExpression.Compile() });
        }

        /// <summary>
        /// The enumerable source
        /// </summary>
        /// <param name="source"><see cref="System.Collections.Generic.IEnumerable{T}"/></param>
        /// <param name="keySelector">A property of T</param>
        /// <param name="sortDirection"><see cref="FI.Core.SortDirection"/></param>
        public static IEnumerable<TSource> Sort<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, SortDirection sortDirection = SortDirection.Ascending)
        {
            switch (sortDirection)
            {
                case SortDirection.Descending:
                    return source.OrderByDescending(keySelector);
            }
            return source.OrderBy(keySelector);
        }

        /// <summary>
        /// Determines whether the <see cref="T:System.Collections.Generic.IEnumerable{T}"/> contains a specific value.
        /// </summary>
        /// <param name="source"></param>
        /// <param name="value">The value to locate in sequence.</param>
        /// <param name="stringComparsion"><see cref="T:System.StringComparison"/></param>
        /// <returns>true if <paramref name="value"/> is found in the <see cref="T:System.Collections.Generic.Ienumerable{T}"/>; otherwise, false.</returns>
        public static bool Contains(this IEnumerable<string> source, string value, StringComparison stringComparsion)
        {
            return source.Any(i => string.Equals(i, value, stringComparsion));
        }

        /// <summary>
        /// Convert enumerable type to another.
        /// </summary>
        /// <param name="source">Original type</param>
        /// <param name="action">Destination type</param>
        public static IEnumerable<TDest> ConvertTo<TSource, TDest>(this IEnumerable<TSource> source, Func<TSource, TDest> action)
        {
            return source.Select(action);
        }
    }


}
